///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef __RenderSystem_H_
#define __RenderSystem_H_

// Precompiler options
#include "OgrePrerequisites.h"

#include "OgreString.h"

#include "OgreTextureUnitState.h"
#include "OgreCommon.h"

#include "OgreMaterialManager.h"
#include "OgreRenderOperation.h"
#include "OgreRenderSystemCapabilities.h"
#include "OgreRenderTarget.h"
#include "OgreRenderTexture.h"
#include "OgreFrameListener.h"
#include "OgreConfigOptionMap.h"
#include "OgreGpuProgram.h"
#include "math/plane.h"
#include "OgreIteratorWrappers.h"

namespace Ogre
{
	
	

	typedef vector<DepthBuffer*>::type DepthBufferVec;
	typedef map< uint16, DepthBufferVec >::type DepthBufferMap;
	typedef map< String, RenderTarget * >::type RenderTargetMap;
	typedef multimap<uchar, RenderTarget * >::type RenderTargetPriorityMap;

	class TextureManager;
	/// Enum describing the ways to generate texture coordinates
	enum TexCoordCalcMethod
	{
		/// No calculated texture coordinates
		TEXCALC_NONE,
		/// Environment map based on vertex normals
		TEXCALC_ENVIRONMENT_MAP,
		/// Environment map based on vertex positions
		TEXCALC_ENVIRONMENT_MAP_PLANAR,
		TEXCALC_ENVIRONMENT_MAP_REFLECTION,
		TEXCALC_ENVIRONMENT_MAP_NORMAL,
		/// Projective texture
		TEXCALC_PROJECTIVE_TEXTURE
	};
	/// Enum describing the various actions which can be taken onthe stencil buffer
	enum StencilOperation
	{
		/// Leave the stencil buffer unchanged
		SOP_KEEP,
		/// Set the stencil value to zero
		SOP_ZERO,
		/// Set the stencil value to the reference value
		SOP_REPLACE,
		/// Increase the stencil value by 1, clamping at the maximum value
		SOP_INCREMENT,
		/// Decrease the stencil value by 1, clamping at 0
		SOP_DECREMENT,
		/// Increase the stencil value by 1, wrapping back to 0 when incrementing the maximum value
		SOP_INCREMENT_WRAP,
		/// Decrease the stencil value by 1, wrapping when decrementing 0
		SOP_DECREMENT_WRAP,
		/// Invert the bits of the stencil buffer
		SOP_INVERT
	};


	
	class _OgreExport RenderSystem : public MemAlloc_RenderSys
	{
	public:
		
		RenderSystem();

		
		virtual ~RenderSystem();

		
		virtual const String& getName(void) const = 0;

		
		virtual ConfigOptionMap& getConfigOptions(void) = 0;

		
		virtual void setConfigOption(const String &name, const String &value) = 0;

		
		virtual HardwareOcclusionQuery* createHardwareOcclusionQuery(void) = 0;

		
		virtual void destroyHardwareOcclusionQuery(HardwareOcclusionQuery *hq);

		
		virtual String validateConfigOptions(void) = 0;

		
		virtual RenderWindow* _initialise(bool autoCreateWindow, const String& windowTitle = "OGRE Render Window");


		
		virtual RenderSystemCapabilities* createRenderSystemCapabilities() const = 0;
 
		
		RenderSystemCapabilities* getMutableCapabilities(){ return mCurrentCapabilities; }

		
		virtual void useCustomRenderSystemCapabilities(RenderSystemCapabilities* capabilities);

		
		virtual void reinitialise(void) = 0;

		
		virtual void shutdown(void);


		
		virtual void setAmbientLight(float r, float g, float b) = 0;

		
		virtual void setShadingType(ShadeOptions so) = 0;

		
		virtual void setLightingEnabled(bool enabled) = 0;

		
		void setWBufferEnabled(bool enabled);

		
		bool getWBufferEnabled(void) const;

		
		virtual RenderWindow* _createRenderWindow(const String &name, unsigned int width, unsigned int height, 
			bool fullScreen, const NameValuePairList *miscParams = 0) = 0;

		
		virtual bool _createRenderWindows(const RenderWindowDescriptionList& renderWindowDescriptions, 
			RenderWindowList& createdWindows);

		
		/**	Create a MultiRenderTarget, which is a render target that renders to multiple RenderTextures
		at once. Surfaces can be bound and unbound at will.
		This fails if mCapabilities->getNumMultiRenderTargets() is smaller than 2.
		*/
		virtual MultiRenderTarget * createMultiRenderTarget(const String & name) = 0; 

		
		virtual void destroyRenderWindow(const String& name);
		
		virtual void destroyRenderTexture(const String& name);
		
		virtual void destroyRenderTarget(const String& name);

		
		virtual void attachRenderTarget( RenderTarget &target );
		
		virtual RenderTarget * getRenderTarget( const String &name );
		
		virtual RenderTarget * detachRenderTarget( const String &name );

		/// Iterator over RenderTargets
		typedef MapIterator<Ogre::RenderTargetMap> RenderTargetIterator;

		
		virtual RenderTargetIterator getRenderTargetIterator(void) {
			return RenderTargetIterator( mRenderTargets.begin(), mRenderTargets.end() );
		}
		
		virtual String getErrorDescription(long errorNumber) const = 0;

		
		void setWaitForVerticalBlank(bool enabled);

		
		bool getWaitForVerticalBlank(void) const;

		
        HardwareVertexBufferSharedPtr getGlobalInstanceVertexBuffer() const;
		
        void setGlobalInstanceVertexBuffer(const HardwareVertexBufferSharedPtr val);
		
        VertexDeclaration* getGlobalInstanceVertexBufferVertexDeclaration() const;
		
        void setGlobalInstanceVertexBufferVertexDeclaration( VertexDeclaration* val);
		
        size_t getGlobalNumberOfInstances() const;
		
        void setGlobalNumberOfInstances(const size_t val);

#ifdef RTSHADER_SYSTEM_BUILD_CORE_SHADERS
		
		void setFixedPipelineEnabled(bool enabled);

		
		bool getFixedPipelineEnabled(void) const;
#endif

		
		virtual void setDepthBufferFor( RenderTarget *renderTarget );

		// ------------------------------------------------------------------------
		//                     Internal Rendering Access
		// All methods below here are normally only called by other OGRE classes
		// They can be called by library user if required
		// ------------------------------------------------------------------------


		
		virtual void _useLights(const LightList& lights, unsigned short limit) = 0;
		
		virtual bool areFixedFunctionLightsInViewSpace() const { return false; }
		
		virtual void _setWorldMatrix(const Matrix4 &m) = 0;
		
		virtual void _setWorldMatrices(const Matrix4* m, unsigned short count);
		
		virtual void _setViewMatrix(const Matrix4 &m) = 0;
		
		virtual void _setProjectionMatrix(const Matrix4 &m) = 0;
		
		virtual void _setTextureUnitSettings(size_t texUnit, TextureUnitState& tl);
		
		virtual void _disableTextureUnit(size_t texUnit);
		
		virtual void _disableTextureUnitsFrom(size_t texUnit);
		
		virtual void _setSurfaceParams(const ColourValue &ambient,
			const ColourValue &diffuse, const ColourValue &specular,
			const ColourValue &emissive, Real shininess,
			TrackVertexColourType tracking = TVC_NONE) = 0;

			
		virtual void _setPointSpritesEnabled(bool enabled) = 0;

		
		virtual void _setPointParameters(Real size, bool attenuationEnabled, 
			Real constant, Real linear, Real quadratic, Real minSize, Real maxSize) = 0;


		/**
		Sets the texture to bind to a given texture unit.

		User processes would not normally call this direct unless rendering
		primitives themselves.

		@param unit The index of the texture unit to modify. Multitexturing 
		hardware can support multiple units (see 
		RenderSystemCapabilites::getNumTextureUnits)
		@param enabled Boolean to turn the unit on/off
		@param texPtr Pointer to the texture to use.
		*/
		virtual void _setTexture(size_t unit, bool enabled, 
			const TexturePtr &texPtr) = 0;
		/**
		Sets the texture to bind to a given texture unit.

		User processes would not normally call this direct unless rendering
		primitives themselves.

		@param unit The index of the texture unit to modify. Multitexturing 
		hardware can support multiple units (see 
		RenderSystemCapabilites::getNumTextureUnits)
		@param enabled Boolean to turn the unit on/off
		@param texname The name of the texture to use - this should have
		already been loaded with TextureManager::load.
		*/
		virtual void _setTexture(size_t unit, bool enabled, const String &texname);

		
		virtual void _setVertexTexture(size_t unit, const TexturePtr& tex);

		/**
		Sets the texture coordinate set to use for a texture unit.

		Meant for use internally - not generally used directly by apps - the Material and TextureUnitState
		classes let you manage textures far more easily.

		@param unit Texture unit as above
		@param index The index of the texture coordinate set to use.
		*/
		virtual void _setTextureCoordSet(size_t unit, size_t index) = 0;

		/**
		Sets a method for automatically calculating texture coordinates for a stage.
		Should not be used by apps - for use by Ogre only.
		@param unit Texture unit as above
		@param m Calculation method to use
		@param frustum Optional Frustum param, only used for projective effects
		*/
		virtual void _setTextureCoordCalculation(size_t unit, TexCoordCalcMethod m, 
			const Frustum* frustum = 0) = 0;

		
		virtual void _setTextureBlendMode(size_t unit, const LayerBlendModeEx& bm) = 0;

		
		virtual void _setTextureUnitFiltering(size_t unit, FilterOptions minFilter,
			FilterOptions magFilter, FilterOptions mipFilter);

		
		virtual void _setTextureUnitFiltering(size_t unit, FilterType ftype, FilterOptions filter) = 0;

		
		virtual void _setTextureLayerAnisotropy(size_t unit, unsigned int maxAnisotropy) = 0;

		
		virtual void _setTextureAddressingMode(size_t unit, const TextureUnitState::UVWAddressingMode& uvw) = 0;

		
		virtual void _setTextureBorderColour(size_t unit, const ColourValue& colour) = 0;

		
		virtual void _setTextureMipmapBias(size_t unit, float bias) = 0;

		
		virtual void _setTextureMatrix(size_t unit, const Matrix4& xform) = 0;

		
		virtual void _setSceneBlending(SceneBlendFactor sourceFactor, SceneBlendFactor destFactor, SceneBlendOperation op = SBO_ADD) = 0;

		
		virtual void _setSeparateSceneBlending(SceneBlendFactor sourceFactor, SceneBlendFactor destFactor, SceneBlendFactor sourceFactorAlpha, 
			SceneBlendFactor destFactorAlpha, SceneBlendOperation op = SBO_ADD, SceneBlendOperation alphaOp = SBO_ADD) = 0;

		
		virtual void _setAlphaRejectSettings(CompareFunction func, unsigned char value, bool alphaToCoverage) = 0;

		
		virtual void _setTextureProjectionRelativeTo(bool enabled, const Vector3& pos);

		
		virtual DepthBuffer* _createDepthBufferFor( RenderTarget *renderTarget ) = 0;

		
		void _cleanupDepthBuffers( bool bCleanManualBuffers=true );

		/**
		* Signifies the beginning of a frame, i.e. the start of rendering on a single viewport. Will occur
		* several times per complete frame if multiple viewports exist.
		*/
		virtual void _beginFrame(void) = 0;
		
		//Dummy structure for render system contexts - implementing RenderSystems can extend
		//as needed
		struct RenderSystemContext { };
		/**
		* Pause rendering for a frame. This has to be called after _beginFrame and before _endFrame.
		* Will usually be called by the SceneManager, don't use this manually unless you know what
		* you are doing.
		*/
		virtual RenderSystemContext* _pauseFrame(void);
		/**
		* Resume rendering for a frame. This has to be called after a _pauseFrame call
		* Will usually be called by the SceneManager, don't use this manually unless you know what
		* you are doing.
		* @param context the render system context, as returned by _pauseFrame
		*/
		virtual void _resumeFrame(RenderSystemContext* context);

		/**
		* Ends rendering of a frame to the current viewport.
		*/
		virtual void _endFrame(void) = 0;
		/**
		Sets the provided viewport as the active one for future
		rendering operations. This viewport is aware of it's own
		camera and render target. Must be implemented by subclass.

		@param target Pointer to the appropriate viewport.
		*/
		virtual void _setViewport(Viewport *vp) = 0;
		
		virtual Viewport* _getViewport(void);

		
		virtual void _setCullingMode(CullingMode mode) = 0;

		virtual CullingMode _getCullingMode(void) const;

		
		virtual void _setDepthBufferParams(bool depthTest = true, bool depthWrite = true, CompareFunction depthFunction = CMPF_LESS_EQUAL) = 0;

		
		virtual void _setDepthBufferCheckEnabled(bool enabled = true) = 0;
		
		virtual void _setDepthBufferWriteEnabled(bool enabled = true) = 0;
		
		virtual void _setDepthBufferFunction(CompareFunction func = CMPF_LESS_EQUAL) = 0;
		
		virtual void _setColourBufferWriteEnabled(bool red, bool green, bool blue, bool alpha) = 0;
		
		virtual void _setDepthBias(float constantBias, float slopeScaleBias = 0.0f) = 0;
		
		virtual void _setFog(FogMode mode = FOG_NONE, const ColourValue& colour = ColourValue::White, Real expDensity = 1.0, Real linearStart = 0.0, Real linearEnd = 1.0) = 0;


		
		virtual void _beginGeometryCount(void);
		
		virtual unsigned int _getFaceCount(void) const;
		
		virtual unsigned int _getBatchCount(void) const;
		
		virtual unsigned int _getVertexCount(void) const;

		
		virtual void convertColourValue(const ColourValue& colour, uint32* pDest);
		
		virtual VertexElementType getColourVertexElementType(void) const = 0;

		
		virtual void _convertProjectionMatrix(const Matrix4& matrix,
			Matrix4& dest, bool forGpuProgram = false) = 0;

		
		virtual void _makeProjectionMatrix(const Radian& fovy, Real aspect, Real nearPlane, Real farPlane, 
			Matrix4& dest, bool forGpuProgram = false) = 0;

		
		virtual void _makeProjectionMatrix(Real left, Real right, Real bottom, Real top, 
			Real nearPlane, Real farPlane, Matrix4& dest, bool forGpuProgram = false) = 0;
		
		virtual void _makeOrthoMatrix(const Radian& fovy, Real aspect, Real nearPlane, Real farPlane, 
			Matrix4& dest, bool forGpuProgram = false) = 0;

		
		virtual void _applyObliqueDepthProjection(Matrix4& matrix, const Plane& plane, 
			bool forGpuProgram) = 0;

		
		virtual void _setPolygonMode(PolygonMode level) = 0;

		
		virtual void setStencilCheckEnabled(bool enabled) = 0;
		
		/*virtual bool hasHardwareStencil(void) = 0;*/

		
		virtual void setStencilBufferParams(CompareFunction func = CMPF_ALWAYS_PASS, 
			uint32 refValue = 0, uint32 mask = 0xFFFFFFFF, 
			StencilOperation stencilFailOp = SOP_KEEP, 
			StencilOperation depthFailOp = SOP_KEEP,
			StencilOperation passOp = SOP_KEEP, 
			bool twoSidedOperation = false) = 0;



		
		virtual void setVertexDeclaration(VertexDeclaration* decl) = 0;
		
		virtual void setVertexBufferBinding(VertexBufferBinding* binding) = 0;

		
		virtual void setNormaliseNormals(bool normalise) = 0;

		/**
		Render something to the active viewport.

		Low-level rendering interface to perform rendering
		operations. Unlikely to be used directly by client
		applications, since the SceneManager and various support
		classes will be responsible for calling this method.
		Can only be called between _beginScene and _endScene

		@param op A rendering operation instance, which contains
		details of the operation to be performed.
		*/
		virtual void _render(const RenderOperation& op);

		
		const RenderSystemCapabilities* getCapabilities(void) const { return mCurrentCapabilities; }


		
		virtual const DriverVersion& getDriverVersion(void) const { return mDriverVersion; }

        
		virtual const String& _getDefaultViewportMaterialScheme(void) const;

		
		virtual void bindGpuProgram(GpuProgram* prg);

		
		virtual void bindGpuProgramParameters(GpuProgramType gptype, 
			GpuProgramParametersSharedPtr params, uint16 variabilityMask) = 0;

		
		virtual void bindGpuProgramPassIterationParameters(GpuProgramType gptype) = 0;
		
		virtual void unbindGpuProgram(GpuProgramType gptype);

		
		virtual bool isGpuProgramBound(GpuProgramType gptype);

		
		virtual void setClipPlanes(const PlaneList& clipPlanes);

		
		virtual void addClipPlane (const Plane &p);
		
		virtual void addClipPlane (Real A, Real B, Real C, Real D);

		
		virtual void resetClipPlanes();

		
		virtual void _initRenderTargets(void);

		
		virtual void _notifyCameraRemoved(const Camera* cam);

		
		virtual void _updateAllRenderTargets(bool swapBuffers = true);
		
		virtual void _swapAllRenderTargetBuffers(bool waitForVsync = true);

		
		virtual void setInvertVertexWinding(bool invert);

		
		virtual bool getInvertVertexWinding(void) const;

		
		virtual void setScissorTest(bool enabled, size_t left = 0, size_t top = 0, 
			size_t right = 800, size_t bottom = 600) = 0;

		
		virtual void clearFrameBuffer(unsigned int buffers, 
			const ColourValue& colour = ColourValue::Black, 
			Real depth = 1.0f, unsigned short stencil = 0) = 0;
		
		virtual Real getHorizontalTexelOffset(void) = 0;
		
		virtual Real getVerticalTexelOffset(void) = 0;

		
		virtual Real getMinimumDepthInputValue(void) = 0;
		
		virtual Real getMaximumDepthInputValue(void) = 0;
		
		virtual void setCurrentPassIterationCount(const size_t count) { mCurrentPassIterationCount = count; }

		
		virtual void setDeriveDepthBias(bool derive, float baseValue = 0.0f,
			float multiplier = 0.0f, float slopeScale = 0.0f)
		{
			mDerivedDepthBias = derive;
			mDerivedDepthBiasBase = baseValue;
			mDerivedDepthBiasMultiplier = multiplier;
			mDerivedDepthBiasSlopeScale = slopeScale;
		}

		/**
         * Set current render target to target, enabling its device context if needed
         */
        virtual void _setRenderTarget(RenderTarget *target) = 0;

		
		class _OgreExport Listener
		{
		public:
			Listener() {}
			virtual ~Listener() {}

			
			virtual void eventOccurred(const String& eventName, 
				const NameValuePairList* parameters = 0) = 0;
		};
		
		virtual void addListener(Listener* l);
		
		virtual void removeListener(Listener* l);

		
		virtual const StringVector& getRenderSystemEvents(void) const { return mEventNames; }

		
		virtual void preExtraThreadsStarted() = 0;

		/* Tell the rendersystem to perform any tasks it needs to directly
		after other threads which might access the rendering API are registered.
		@see RenderSystem::preExtraThreadsStarted
		*/
		virtual void postExtraThreadsStarted() = 0;

		
		virtual void registerThread() = 0;

		
		virtual void unregisterThread() = 0;

		/**
		* Gets the number of display monitors.
		@see Root::getDisplayMonitorCount
		*/
		virtual unsigned int getDisplayMonitorCount() const = 0;
	protected:

		
		DepthBufferMap	mDepthBufferPool;

		
		RenderTargetMap mRenderTargets;
		
		RenderTargetPriorityMap mPrioritisedRenderTargets;
		
		RenderTarget * mActiveRenderTarget;

		
		GpuProgramParametersSharedPtr mActiveVertexGpuProgramParameters;
		GpuProgramParametersSharedPtr mActiveGeometryGpuProgramParameters;
		GpuProgramParametersSharedPtr mActiveFragmentGpuProgramParameters;

		// Texture manager
		// A concrete class of this will be created and
		// made available under the TextureManager singleton,
		// managed by the RenderSystem
		TextureManager* mTextureManager;

		// Active viewport (dest for future rendering operations)
		Viewport* mActiveViewport;

		CullingMode mCullingMode;

		bool mVSync;
		unsigned int mVSyncInterval;
		bool mWBuffer;

		size_t mBatchCount;
		size_t mFaceCount;
		size_t mVertexCount;

		/// Saved manual colour blends
		ColourValue mManualBlendColours[OGRE_MAX_TEXTURE_LAYERS][2];

		bool mInvertVertexWinding;

		/// Texture units from this upwards are disabled
		size_t mDisabledTexUnitsFrom;

		/// number of times to render the current state
		size_t mCurrentPassIterationCount;
		size_t mCurrentPassIterationNum;
		/// Whether to update the depth bias per render call
		bool mDerivedDepthBias;
		float mDerivedDepthBiasBase;
		float mDerivedDepthBiasMultiplier;
		float mDerivedDepthBiasSlopeScale;

        /// a global vertex buffer for global instancing
        HardwareVertexBufferSharedPtr mGlobalInstanceVertexBuffer;
        /// a vertex declaration for the global vertex buffer for the global instancing
        VertexDeclaration* mGlobalInstanceVertexBufferVertexDeclaration;
        /// the number of global instances (this number will be multiply by the render op instance number) 
        size_t mGlobalNumberOfInstances;

#ifdef RTSHADER_SYSTEM_BUILD_CORE_SHADERS
		/// is fixed pipeline enabled
		bool mEnableFixedPipeline;
#endif

		
		bool updatePassIterationRenderState(void);

		/// List of names of events this rendersystem may raise
		StringVector mEventNames;

		/// Internal method for firing a rendersystem event
		virtual void fireEvent(const String& name, const NameValuePairList* params = 0);

		typedef list<Listener*>::type ListenerList;
		ListenerList mEventListeners;

		typedef list<HardwareOcclusionQuery*>::type HardwareOcclusionQueryList;
		HardwareOcclusionQueryList mHwOcclusionQueries;

		bool mVertexProgramBound;
		bool mGeometryProgramBound;
		bool mFragmentProgramBound;

		// Recording user clip planes
		PlaneList mClipPlanes;
		// Indicator that we need to re-set the clip planes on next render call
		bool mClipPlanesDirty;

		/// Used to store the capabilities of the graphics card
		RenderSystemCapabilities* mRealCapabilities;
		RenderSystemCapabilities* mCurrentCapabilities;
		bool mUseCustomCapabilities;

		/// Internal method used to set the underlying clip planes when needed
		virtual void setClipPlanesImpl(const PlaneList& clipPlanes) = 0;

		
		virtual void initialiseFromRenderSystemCapabilities(RenderSystemCapabilities* caps, RenderTarget* primary) = 0;


		DriverVersion mDriverVersion;

		bool mTexProjRelative;
		Vector3 mTexProjRelativeOrigin;



	};
	
	
}

#endif
